//
//  DGKeychain.m
//  DGKeychain
//
//  Created by Jinxiao on 12/2/13.
//  Copyright (c) 2013 debugeek. All rights reserved.
//

#import "DGKeychain.h"
#import <Security/Security.h>

static NSString *DGKeychainErrorDomain = @"DGKeychainErrorDomain";

@implementation DGKeychain

+ (NSString *)queryPasswordForUsername:(NSString *)username serviceName:(NSString *)serviceName error:(NSError **)error
{
    if(!username || !serviceName)
    {
        if(error != nil)
        {
            *error = [NSError errorWithDomain:DGKeychainErrorDomain code:-2000 userInfo:nil];
        }
        
        return nil;
    }
    
    if(error != nil)
    {
        *error = nil;
    }
    
    NSArray *keys = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClass, kSecAttrAccount, kSecAttrService, nil];
    NSArray *objects = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClassGenericPassword, username, serviceName, nil];
    NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys];
    
    NSMutableDictionary *attributeQuery = [query mutableCopy];
    [attributeQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnAttributes];
    CFTypeRef attrResult = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributeQuery, &attrResult);
    
    if(status != noErr)
    {
        if(error != nil && status != errSecItemNotFound)
        {
            *error = [NSError errorWithDomain:DGKeychainErrorDomain code:status userInfo:nil];
        }
        
        return nil;
    }
    
    NSMutableDictionary *passwordQuery = [query mutableCopy];
    [passwordQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
    CFTypeRef resData = NULL;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)passwordQuery, (CFTypeRef *)&resData);
    NSData *resultData = (__bridge_transfer NSData *)resData;
    if(status != noErr)
    {
        if(status == errSecItemNotFound)
        {
            if(error != nil)
            {
                *error = [NSError errorWithDomain:DGKeychainErrorDomain code:-1999 userInfo:nil];
            }
        }
        else
        {
            if(error != nil)
            {
                *error = [NSError errorWithDomain:DGKeychainErrorDomain code:status userInfo:nil];
            }
        }
        
        return nil;
    }
    
    NSString *password = nil;
    if(resultData)
    {
        password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
    }
    else
    {
        if(error != nil)
        {
            *error = [NSError errorWithDomain:DGKeychainErrorDomain code:-1999 userInfo:nil];
        }
    }
    
    return password;
}

+ (BOOL)storeUsername:(NSString *)username password:(NSString *)password serviceName:(NSString *)serviceName updateExisting:(BOOL)updateExisting error:(NSError **)error

{
    if(!username || !password || !serviceName)
    {
        if(error != nil)
        {
            *error = [NSError errorWithDomain:DGKeychainErrorDomain code:-2000 userInfo:nil];
        }
        
        return NO;
    }
    
    NSError *getError = nil;
    NSString *existingPassword = [self queryPasswordForUsername:username serviceName:serviceName error:&getError];
    
    if([getError code] == -1999)
    {
        getError = nil;
        
        [self deleteItemForUsername:username serviceName:serviceName error:&getError];
        
        if([getError code] != noErr)
        {
            if(error != nil)
            {
                *error = getError;
            }
            
            return NO;
        }
    }
    else if([getError code] != noErr)
    {
        if(error != nil)
        {
            *error = getError;
        }
        
        return NO;
    }
    
    if(error != nil)
    {
        *error = nil;
    }
    
    OSStatus status = noErr;
    
    if(existingPassword)
    {
        if(![existingPassword isEqualToString:password] && updateExisting)
        {
            NSArray *keys = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClass, kSecAttrService, kSecAttrLabel, kSecAttrAccount, nil];
            
            NSArray *objects = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClassGenericPassword, serviceName, serviceName, username, nil];
            
            NSDictionary *query = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
            
            status = SecItemUpdate((__bridge_retained CFDictionaryRef)query, (__bridge_retained CFDictionaryRef)[NSDictionary dictionaryWithObject:[password dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge_transfer NSString *)kSecValueData]);
        }
    }
    else
    {
        NSArray *keys = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClass, kSecAttrService, kSecAttrLabel, kSecAttrAccount, kSecValueData, nil];
        
        NSArray *objects = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClassGenericPassword, serviceName, serviceName, username, [password dataUsingEncoding:NSUTF8StringEncoding], nil];
        
        NSDictionary *query = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
        
        status = SecItemAdd((__bridge_retained CFDictionaryRef)query, NULL);
    }
    
    if(error != nil && status != noErr)
    {
        *error = [NSError errorWithDomain:DGKeychainErrorDomain code:status userInfo:nil];
        return NO;
    }
    
    return YES;
}

+ (BOOL)deleteItemForUsername:(NSString *)username serviceName:(NSString *)serviceName error:(NSError **)error
{
    if(!username || !serviceName)
    {
        if(error != nil)
        {
            *error = [NSError errorWithDomain:DGKeychainErrorDomain code:-2000 userInfo:nil];
        }
        
        return NO;
    }
    
    if(error != nil)
    {
        *error = nil;
    }
    
    NSArray *keys = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil];
    NSArray *objects = [[NSArray alloc] initWithObjects:(__bridge_transfer NSString *)kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil];
    NSDictionary *query = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
    
    if(error != nil && status != noErr)
    {
        *error = [NSError errorWithDomain:DGKeychainErrorDomain code:status userInfo:nil];
        return NO;
    }
    
    return YES;
}

@end